use std::time::Duration;
use std::any::Any;
use async_trait::async_trait;
use tracing::{debug, info, span, Level, warn};
use protoactor_rs::{
    Actor, ActorContext, ActorError, PID, Props, ActorSystem, 
    logging, 
};

// 定义一个简单的消息
#[derive(Debug, Clone)]
struct Ping {
    count: u32,
}

// 定义一个简单的响应
#[derive(Debug, Clone)]
struct Pong {
    count: u32,
}

// 定义一个Actor
struct LoggingActor {
    name: String,
}

#[async_trait]
impl Actor for LoggingActor {
    type Context = protoactor_rs::actor::DefaultActorContext;
    
    async fn started(&self, _ctx: &mut Self::Context) -> Result<(), ActorError> {
        info!(actor_name = self.name, "Actor started");
        Ok(())
    }
    
    async fn stopping(&self, _ctx: &mut Self::Context) -> Result<(), ActorError> {
        info!(actor_name = self.name, "Actor stopping");
        Ok(())
    }
    
    async fn receive(&self, ctx: &mut Self::Context, msg: Box<dyn Any + Send>) -> Result<(), ActorError> {
        // 在receive方法中创建一个span
        let span = span!(Level::INFO, "message_processing", actor = self.name);
        let _enter = span.enter();
        
        // 尝试将消息转换为Ping
        if let Ok(ping) = msg.downcast::<Ping>() {
            debug!(message_type = "Ping", count = ping.count, "收到Ping消息");
            
            // 创建一个Pong响应
            let pong = Pong {
                count: ping.count + 1,
            };
            
            // 返回响应
            ctx.respond(pong);
            
            // 如果count大于0，则继续发送消息给自己
            if ping.count > 0 {
                let next_ping = Ping {
                    count: ping.count - 1,
                };
                
                // 获取自己的PID并发送下一个消息
                let pid = ctx.self_pid().clone(); // 获取并克隆PID
                
                // 通过ActorSystem发送延迟消息
                tokio::spawn(async move {
                    tokio::time::sleep(Duration::from_millis(100)).await;
                    let system = ActorSystem::new().unwrap();
                    if let Err(e) = system.send(&pid, next_ping).await {
                        tracing::error!("Failed to send next ping: {:?}", e);
                    }
                });
            }
        } else {
            info!("收到未知消息类型");
        }
        
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 方法1: 使用基本日志初始化
    // 设置库日志级别为DEBUG，应用日志级别为INFO
    logging::init_global_logger(
        Some(tracing::Level::DEBUG),
        Some("logging_example=info"),
    )?;
    
    /*
    // 方法2: 使用高级日志初始化
    logging::init_advanced_logger(
        Some("protoactor_rs=debug,logging_example=info"),
        Some(tracing_subscriber::fmt::format::FmtSpan::ACTIVE),
        true,  // 包含文件信息
        true,  // 包含行号
        true,  // 使用ANSI颜色
    )?;
    */
    
    /*
    // 方法3: 使用JSON日志 (需要启用json-logging特性)
    // logging::init_json_logger(Some("info"))?;
    */
    
    // 创建Actor系统
    let system = ActorSystem::new()?;
    
    // 创建三个Actor实例
    let props1 = Props::from_producer(|| LoggingActor { name: "actor1".to_string() });
    let props2 = Props::from_producer(|| LoggingActor { name: "actor2".to_string() });
    let props3 = Props::from_producer(|| LoggingActor { name: "actor3".to_string() });
    
    // 生成Actor
    let pid1 = system.spawn::<LoggingActor>(props1).await?;
    let pid2 = system.spawn::<LoggingActor>(props2).await?;
    let pid3 = system.spawn::<LoggingActor>(props3).await?;
    
    // 向第一个Actor发送一个Ping消息，触发链式反应
    system.send(&pid1, Ping { count: 5 }).await?;
    
    // 发送请求并等待响应
    let span = span!(Level::INFO, "request_response");
    let _enter = span.enter();
    
    let pong = system.request::<Pong>(&pid2, Ping { count: 0 }, Duration::from_secs(1)).await?;
    info!(response_count = pong.count, "收到Pong响应");
    
    // 等待几秒钟，让所有消息都处理完
    tokio::time::sleep(Duration::from_millis(1000)).await;
    
    // 停止所有Actor
    system.stop(&pid1).await?;
    system.stop(&pid2).await?;
    system.stop(&pid3).await?;
    
    // 等待所有Actor都停止
    tokio::time::sleep(Duration::from_millis(100)).await;
    
    Ok(())
} 